import time
from typing import List, Tuple, Any, Dict

from fuzzer.GreyBoxFuzzer import GreyBoxFuzzer
from schedule.CutoffPowerSchedule import CutoffExponentialSchedule
from runner.FunctionCoverageRunner import FunctionCoverageRunner
from runner.Runner import Runner

class CutoffGreyBoxFuzzer(GreyBoxFuzzer):
    """基于截断指数调度的灰盒Fuzzer"""

    def __init__(self, seeds: List[str], beta: float = 2.0, M: float = 100.0, is_print: bool = True):
        schedule = CutoffExponentialSchedule(beta, M)
        super().__init__(seeds, schedule, False)
        
        self.covered_paths = set()  # 覆盖的路径集合
        self.last_new_path_time = self.start_time
        self.path_frequency: Dict[int, int] = {}  # {path_id: frequency}
        schedule.bind_frequency(self.path_frequency)

        if is_print:
            print("""
┌───────────────────────┬───────────────────────┬───────────────────────┬───────────────────┬───────────────────┬────────────────┬───────────────────┐
│        Run Time       │     Last New Path     │    Last Uniq Crash    │    Total Execs    │    Total Paths    │  Uniq Crashes  │   Covered Lines   │
├───────────────────────┼───────────────────────┼───────────────────────┼───────────────────┼───────────────────┼────────────────┼───────────────────┤""")

    def print_stats(self):
        def format_seconds(seconds):
            hours = int(seconds) // 3600
            minutes = int(seconds % 3600) // 60
            remaining_seconds = int(seconds) % 60
            return f"{hours:02d}:{minutes:02d}:{remaining_seconds:02d}"

        template = """│{runtime}│{path_time}│{crash_time}│{total_exec}│{total_path}│{uniq_crash}│{covered_line}│
├───────────────────────┼───────────────────────┼───────────────────────┼───────────────────┼───────────────────┼────────────────┼───────────────────┤"""
        template = template.format(
            runtime=format_seconds(time.time() - self.start_time).center(23),
            path_time=format_seconds(self.last_new_path_time - self.start_time).center(23),
            crash_time=format_seconds(self.last_crash_time - self.start_time).center(23),
            total_exec=str(self.total_execs).center(19),
            total_path=str(len(self.covered_paths)).center(19),
            uniq_crash=str(len(set(self.crash_map.values()))).center(16),
            covered_line=str(len(self.covered_line)).center(19)
        )
        print(template)

    def run(self, runner: FunctionCoverageRunner) -> Tuple[Any, str]:
        """执行测试并更新路径统计"""
        result, outcome = super().run(runner)
        
        # 更新路径频率
        path_id = hash(frozenset(runner.coverage()))
        self.path_frequency[path_id] = self.path_frequency.get(path_id, 0) + 1

        # 处理新路径
        if outcome == Runner.PASS and path_id not in self.covered_paths:
            self.covered_paths.add(path_id)
            self.last_new_path_time = time.time()
            self.schedule.mark_seed(path_id)  # 标记为种子路径

        return result, outcome